package com.sanri.tools.modules.minio.service;

import com.alibaba.fastjson.JSON;
import com.sanri.tools.modules.core.dtos.param.PageParam;
import com.sanri.tools.modules.core.exception.ToolException;
import com.sanri.tools.modules.core.service.connect.ActiveConnectManage;
import com.sanri.tools.modules.core.service.connect.ConnectService;
import com.sanri.tools.modules.minio.service.dtos.Minio;
import com.sanri.tools.modules.minio.service.dtos.MinioConnect;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import io.minio.messages.NotificationConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class MinioService implements ActiveConnectManage {

    @Autowired
    private ConnectService connectService;

    public static final String MODULE = "minio";

    /**
     * connName => MinioClient
     */
    private static final Map<String, MinioClient> minioClientMap = new ConcurrentHashMap<>();

    /**
     * 存储桶列表
     * @param connName 连接名
     * @return
     */
    public List<Minio.Bucket> buckets(String connName) throws Exception {
        MinioClient minioClient = minioClient(connName);
        List<Bucket> buckets = minioClient.listBuckets();

        List<Minio.Bucket> bucketList = new ArrayList<>();
        for (Bucket bucket : buckets) {
            Minio.Bucket outBucket = new Minio.Bucket();
            outBucket.setName(bucket.name());
            outBucket.setCreateDate(Date.from(bucket.creationDate().toInstant()));
            bucketList.add(outBucket);

            // 获取 bucket 属性
            String bucketPolicy = minioClient.getBucketPolicy(bucket.name());
            String bucketLifeCycle = minioClient.getBucketLifeCycle(bucket.name());
            NotificationConfiguration bucketNotification = minioClient.getBucketNotification(bucket.name());
            outBucket.setBucketPolicy(bucketPolicy);
            outBucket.setBucketLifeCycle(bucketLifeCycle);
            outBucket.setNotificationConfiguration(bucketNotification);
        }
        return bucketList;
    }

    /**
     * 图片搜索
     * @param connName 连接名
     * @param bucketName 存储桶
     * @param pageParam 分页参数
     * @return
     */
    public List<Minio.ImageInfo> searchImages(String connName, String bucketName, String prefix, PageParam pageParam) throws Exception {
        final Integer start = pageParam.getStart();
        final Integer pageSize = pageParam.getPageSize();

        MinioClient minioClient = minioClient(connName);
        Iterable<Result<Item>> results = minioClient.listObjects(bucketName, prefix,false , true);
        Iterator<Result<Item>> resultIterator = results.iterator();
        List<Minio.ImageInfo> imageInfos = new ArrayList<>();
        int index = 0;
        while (resultIterator.hasNext()){
            if (index ++ < start){
                // skip page data
                continue;
            }
            Result<Item> itemResult = resultIterator.next();
            Item item = itemResult.get();
            Minio.ImageInfo imageInfo = Minio.convertToImageInfo(item);
            imageInfos.add(imageInfo);
            if (imageInfos.size() >= pageSize){
                break;
            }
        }
        return imageInfos;
    }

    /**
     * 获取文件信息
     * @param connName 连接名
     * @param bucketName 存储桶名
     * @param objectName 图片唯一标识
     * @return
     * @throws Exception
     */
    public Minio.ImageInfo fileInfo(String connName, String bucketName, String objectName) throws Exception {
        MinioClient minioClient = minioClient(connName);
        ObjectStat objectStat = minioClient.statObject(bucketName, objectName);
        Minio.ImageInfo imageInfo = Minio.convertToImageInfo(objectStat);
        return imageInfo;
    }
    /**
     * 加载一张图
     * @param connName 连接名
     * @param bucketName 存储桶名
     * @param objectName 图片唯一标识
     * @return
     */
    public byte [] loadImage(String connName, String bucketName,String objectName)throws Exception{
        MinioClient minioClient = minioClient(connName);
        try(InputStream inputStream = minioClient.getObject(bucketName, objectName);){
            byte[] bytes = IOUtils.toByteArray(inputStream);
            return bytes;
        }
    }

    /**
     * 上传一张图
     * @return
     */
    public String uploadFiles(String connName, String bucketName, String prefix, MultipartFile multipartFile) throws Exception {
        MinioClient minioClient = minioClient(connName);
        PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
        String objectName = prefix +"/"+ multipartFile.getOriginalFilename();
        minioClient.putObject(bucketName, objectName,multipartFile.getInputStream(),putObjectOptions);
        return objectName;
    }

    MinioClient minioClient(String connName) throws Exception{
        if (minioClientMap.containsKey(connName)){
            return minioClientMap.get(connName);
        }
        String content = connectService.loadContent(MODULE, connName);
        MinioConnect minioConnect = JSON.parseObject(content, MinioConnect.class);
        MinioClient minioClient = new MinioClient(minioConnect.getEndpoint(),minioConnect.getAccessKey(),minioConnect.getSecretKey());

        try {
            List<Bucket> buckets = minioClient.listBuckets();
        }catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException | InvalidKeyException | InvalidResponseException | NoSuchAlgorithmException | XmlParserException e){
            log.error("[{}]连接失败， 原因为[{}]",e.getMessage());
           throw new ToolException(connName +" 连接失败, 原因为:"+e.getMessage());
        }

        minioClientMap.put(connName, minioClient);
        return minioClient;
    }

    @Override
    public String module() {
        return MODULE;
    }

    @Override
    public Set<String> activeConnects() {
        return minioClientMap.keySet();
    }

    @Override
    public Class<?> connectClass() {
        return MinioClient.class;
    }

    @Override
    public boolean kill(String id) {
        MinioClient minioClient = minioClientMap.remove(id);
        if (minioClient != null){
            return true;
        }
        return false;
    }

    @Override
    public void reconnect(String id) {
        // ignore
    }
}
